/**
 * 
 */
package com.example.downloadmanagerdemo.download;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;

/**
 * 下载线程
 */
public class DownloadTask extends Thread implements DownloadTaskImp {
	private File mSaveFile;
	private DownloadInfo mDownloadInfo;
	private long mDownloadedLength;
	private DownloadStatusCallback mCallback;
	private DownloadMMCache mCache;
	private boolean mUseMMCache = false;// 表示是否使用缓存
	private boolean mKeeploading = true;// 用于控制暂停的flag
	private String mPackageName;

	public DownloadTask(String savePath, DownloadInfo info) {
		mSaveFile = new File(savePath + ".apk");
		mDownloadInfo = info;
		mPackageName = info.getPackageName();
		mDownloadedLength = DownloadRecordHelper.getInstance().readSavedSize(mPackageName);
		mDownloadInfo.setSavePath(savePath);
	}

	public void setDownloadStatusCallback(DownloadStatusCallback callback) {
		mCallback = callback;
	}

	public void useMMCache(boolean use) {
		mUseMMCache = use;
	}

	public void cancel() {
		mKeeploading = false;
	}

	public String packageName() {
		return mPackageName;
	}

	@Override
	public void run() {
		DownloadInfo info = mDownloadInfo;
		String downloadUrl = info.getDownloadUrl();
		long start = mDownloadedLength;
		HttpURLConnection connection = null;
		RandomAccessFile accessFile = null;
		InputStream is = null;
		try {
			connection = createDefaultHttpURLConnection(downloadUrl);
			long length = getContentLength(downloadUrl);
			if (length <= 0) {
				onUnKnowError();
			}
			info.setSize(length);
			DownloadRecordHelper.getInstance().writeSize(length, mPackageName);
			if (start == length - 1) {
				onFinished();
				return;
			}
			if (start > 0) {
				connection.setRequestProperty("Range", "bytes=" + start + "-" + (length - 1));
			}
			Log.i(DownloadConfig.LOG_TAG, "mSaveFile = " + mSaveFile + " length = " + length);
			accessFile = new RandomAccessFile(mSaveFile, "rw");
			accessFile.seek(start);
			accessFile.setLength(length);
			if (mUseMMCache) {
				mCache = new DownloadMMCache(new ByteArrayOutputStream(), accessFile,
						DownloadConfig.DEFAULT_DOWNLOAD_MM_CACHED_SIZE, mDownloadedLength, mPackageName);
			}
			connection.connect();
			is = connection.getInputStream();
			byte[] temp = new byte[2 * 1024];
			int readed = 0;
			onStart();
			while ((readed = is.read(temp, 0, temp.length)) != -1) {
				// 暂停检测
				if (!mKeeploading) {
					onPause();
					break;
				}
				// SD卡移除检测
				if (!DownloadUtils.isSDCardEnable()) {
					onSDCardMounted();
					break;
				}
				// SD卡空间检测
				if (!DownloadUtils.isSDCardSpaceEnough()) {
					onSpaceNotEnough();
					break;
				}
				Log.i(DownloadConfig.LOG_TAG, "write data");
				if (mUseMMCache) {// 采用缓存策略，减少文件写入频率
					mCache.write(temp, 0, readed);
				} else {
					accessFile.write(temp, 0, readed);
					updateRecord(mDownloadedLength);
				}
				mDownloadedLength += readed;
				int progress = (int) (mDownloadedLength * 100 / length);
				if (progress - info.getProgress() >= 1) {
					Log.i(DownloadConfig.LOG_TAG, "updateProgress");
					onProgressChanged(progress);
					info.setProgress(progress);
					info.setDownloadedSize(mDownloadedLength);
				}
			}
			if (mUseMMCache) {
				mCache.flush();
			}
			recycleResources(connection, accessFile, is);
			if (mDownloadedLength == length) {
				onFinished();
			}
		} catch (MalformedURLException e) {
			e.printStackTrace();
			onNetworkError();
		} catch (IOException e) {
			e.printStackTrace();
			onReadError();
		} finally {
			if (mUseMMCache) {
				try {
					mCache.flush();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			try {
				recycleResources(connection, accessFile, is);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	private HttpURLConnection createDefaultHttpURLConnection(String downloadUrl) throws IOException,
			MalformedURLException {
		HttpURLConnection connection;
		connection = (HttpURLConnection) new URL(downloadUrl).openConnection();
		connection.setRequestProperty("Connection", "Keep-Alive");
		connection.setRequestProperty("Charset", "UTF-8");
		connection.setRequestProperty("Accept-Language", "zh-CN");
		connection.setRequestProperty("User-Agent", "Android");
		return connection;
	}

	private void recycleResources(HttpURLConnection connection, RandomAccessFile accessFile, InputStream is)
			throws IOException {
		if (accessFile != null) {
			accessFile.close();
		}
		if (is != null) {
			is.close();
		}
		if (connection != null) {
			connection.disconnect();
		}
	}

	public long getContentLength(String url) throws IOException {
		URL u = new URL(url);
		HttpURLConnection conn = (HttpURLConnection) u.openConnection();
		long l = conn.getContentLength();
		conn.disconnect();
		return l;
	}

	private void runOnMainThread(Runnable run) {
		new Handler(Looper.getMainLooper()).post(run);
	}

	private void updateRecord(long saveSize) {
		DownloadRecordHelper.getInstance().writeSavedSize(saveSize, mPackageName);
	}

	@Override
	public void onStart() {
		runOnMainThread(new Runnable() {
			@Override
			public void run() {
				if (mCallback != null) {
					mCallback.onDownloadStatusChanged(DownloadStatus.STATUS_DOWNLOADING, mPackageName);
				}
			}
		});
	}

	@Override
	public void onProgressChanged(final int progress) {
		runOnMainThread(new Runnable() {
			@Override
			public void run() {
				if (mCallback != null) {
					mCallback.onDownloadProgressChanged(progress, mPackageName);
				}
			}
		});
	}

	@Override
	public void onPause() {
		runOnMainThread(new Runnable() {
			@Override
			public void run() {
				if (mCallback != null) {
					mCallback.onDownloadStatusChanged(DownloadStatus.STATUS_PAUSE, mPackageName);
				}
			}
		});
	}

	@Override
	public void onNetworkError() {
		runOnMainThread(new Runnable() {
			@Override
			public void run() {
				if (mCallback != null) {
					DownloadError error = new DownloadError();
					error.errorCode = DownloadError.ERROR_NETWORK;
					mCallback.onDownloadError(error);
				}
			}
		});
	}

	@Override
	public void onSDCardMounted() {
		runOnMainThread(new Runnable() {
			@Override
			public void run() {
				if (mCallback != null) {
					DownloadError error = new DownloadError();
					error.errorCode = DownloadError.ERROR_SDCARD_UNMOUNTED;
					mCallback.onDownloadError(error);
				}
			}
		});
	}

	@Override
	public void onSpaceNotEnough() {
		runOnMainThread(new Runnable() {
			@Override
			public void run() {
				if (mCallback != null) {
					DownloadError error = new DownloadError();
					error.errorCode = DownloadError.ERROR_SDCARD_SPACE_NOT_ENOUGH;
					mCallback.onDownloadError(error);
				}
			}
		});
	}

	@Override
	public void onUnKnowError() {
		runOnMainThread(new Runnable() {
			@Override
			public void run() {
				if (mCallback != null) {
					DownloadError error = new DownloadError();
					error.errorCode = DownloadError.ERROR_UNKNOW;
					mCallback.onDownloadError(error);
				}
			}
		});
	}

	@Override
	public void onFinished() {
		runOnMainThread(new Runnable() {
			@Override
			public void run() {
				if (mCallback != null) {
					mCallback.onDownloadStatusChanged(DownloadStatus.STATUS_SUCCESS, mPackageName);
					DownloadRecordHelper.getInstance().signFinish(mPackageName);
				}
			}
		});
	}

	@Override
	public void onReadError() {
		runOnMainThread(new Runnable() {
			@Override
			public void run() {
				if (mCallback != null) {
					DownloadError error = new DownloadError();
					error.errorCode = DownloadError.ERROR_READ;
					mCallback.onDownloadError(error);
				}
			}
		});
	}

	@Override
	public void onPrepared() {
		runOnMainThread(new Runnable() {
			@Override
			public void run() {
				if (mCallback != null) {
					mCallback.onDownloadStatusChanged(DownloadStatus.STATUS_WAIT, mPackageName);
				}
			}
		});
	}
}
